package mulegame;

import java.io.File;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.swing.*;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.io.PrintWriter;
import java.util.Scanner;

import javax.swing.JFileChooser;

/**
 * GraphicsControl is the Presenter in our MVP architecture. This class handles
 * the game's main frame and what is displayed on it. It also keeps track of the
 * current game "phase" and handles changing screens upon user and game events.
 * 
 * @author AREA 29
 *
 */
public class GraphicsControl{
    
    private JFrame gameFrame;
    private GameEngine engine;
    private JPanel start, townScreen, pubScreen, storeScreen, panelScores, panelResc, panelInstr;
    private MainScreen mainScreen;
    private Dimension windowsize = new Dimension(900, 650);
    private enum Phase {LAND_AUCTION, LAND_AUCTION_OVER, MAIN_TURN, STORE, PLACE_MULE, INIT}; 
    private Phase phase;
    private int turnCount = 0;
    private int passCount = 0;
    
    /**
     * Makes a GraphicsControl object using the provided GameEngine.
     * 
     * @param eng The GameEngine to use
     */
    public GraphicsControl(GameEngine eng){
        
        //access to model
        engine = eng;
        
        //init the JFrame
        gameFrame = new JFrame("Coffee Mule");
        gameFrame.setResizable(false);
	    gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	    
	    //init some static screens
        start = new StartScreen(this);
        
        //init some panels
        panelResc = new Resources();
        panelInstr = new Instructions();
        
        phase = Phase.INIT;
    }
    
    /**
     * Displays a new JPanel on the main game frame, along with standard info 
     * panels (Scores, Resources, Instructions).
     * 
     * @param panel The panel to display
     * @param addInfoPanels If true, puts the info panels on
     */
    private void newPanelForFrame(JPanel panel, boolean addInfoPanels){
        if(addInfoPanels){
            panel.add(panelScores);
            panel.add(panelResc);
            panel.add(panelInstr);
        }
        gameFrame.getContentPane().removeAll();
        gameFrame.getContentPane().add(panel);
        gameFrame.pack();
        gameFrame.setSize(windowsize);
	    gameFrame.setLocationRelativeTo(null); 
        gameFrame.setVisible(true);
        //gameFrame.revalidate();
    }
    
    // ########################################
    // #                                      #
    // #       screen changing methods        #
    // #                                      #
    // ########################################
    
    /**
     * Changes the current screen to the Start screen.
     */
    public void beginStartScreen(){
        Logger.println("beginStartScreen");
        newPanelForFrame(start, false);
        
    }
    /**
     * Receives the information collected by the Start screen and uses that 
     * information to intitialize the game
     *
     * @param players The selected number of players playing (2 to 4)
     * @param difficulty The selected difficulty for the game
     * @param mapType The selected map type
     */
    public void finishStartScreen(int players, GameConfig.Difficulty difficulty, int mapType){
        Logger.println("numPlayers: " + new Integer(players).toString() + "\nDifficulty: " + difficulty.toString() + "\nmapType: " + new Integer(mapType).toString()); 
        
        //give engine the info collected from startscreen
        engine.setNumPlayers(players);
        engine.setDifficulty(difficulty);
        engine.makeMap(mapType);
        beginPlayerScreen();
    }
    
    
   /**
    * Changes the current screen to the Player screen. 
    *
    */
    public void beginPlayerScreen(){
        Logger.println("beginPlayerScreen");
        JPanel players = new PlayerScreen(this, engine.getNumPlayers());
        newPanelForFrame(players, false);
    }
    
    
    /**
     * Receives information collected by the Player screen and sets up the
     * game accordingly.
     *
     * @param names Players' chosen names
     * @param races Players' chosen races
     * @param colors Players' chosen colors
     */
    public void finishPlayerScreen(String[] names, Player.Race[] races, Color[] colors){
        Logger.println("finished with Player Screen");
        
        //loop through arrays and make players using the info in those arrays
        for (int i = 0; i < names.length; i++){
            Logger.println(
                "Player " + new Integer(i + 1) +
                "\n\tName: " + names[i] +
                "\n\tRace: " + races[i].name +
                "\n\tColor: " + colors[i].toString()
            );
            engine.makePlayer(names[i], races[i], colors[i]);
            incrTurn();
        }
        
        //establish the turn order for the first turn
        engine.sortPlayers();
        panelScores = new Scores(engine.getPlayers());
        JOptionPane.showMessageDialog(gameFrame, engine.getCurrentPlayerName() + ": start the game" , "Here we go!", JOptionPane.WARNING_MESSAGE);                
        startLandAuction();
    }
    
    /**
     * Begins the Land Auction phase of the round
     */
    public void startLandAuction(){
        ((Resources)panelResc).usePlayer(engine.getCurrPlayer());
        mainScreen = new MainScreen(this, 30*1000, engine.getMap());//30 seconds for initial land selection
        switchPhase(Phase.LAND_AUCTION);
        newPanelForFrame(mainScreen, true);
    }
    
    /**
     * Begins the Main Turn phase of the round
     */
    public void beginMainTurn(){
        
        int turnTime = engine.getPlayerTurnTime(turnCount);
        
        //assuming GameEngine passes back the turn time in miliseconds
        ((Resources)panelResc).usePlayer(engine.getCurrPlayer());
        mainScreen = new MainScreen(this, turnTime, engine.getMap());
        switchPhase(Phase.MAIN_TURN);
        setInstructionText("Click the town to get started");
        newPanelForFrame(mainScreen, true);
    }
    
    
    /**
     * Cleans up after the Main Screen and shows the next screen
     */
    public void finishMainScreen(){
        Logger.println("done with main screen");
        ((TimedScreen)mainScreen).stopTimers();
        incrTurn();
        showNextScreen();
    }
    
    
    // ########################################
    // #                                      #
    // #        button click handlers         #
    // #                                      #
    // ########################################

    /**
     * Handles changing the screen back to Main Screen. This should only get
     * called when the Town Screen's back button is clicked
     */
    public void backToMainScreen(){
        newPanelForFrame(mainScreen, true);
        setInstructionText("You are back at the Map.  What would you like to do?");
    }
    
    /**
     * Handles changing the screen back to Town Screen. This gets called when
     * Pub/Store/LandOffice Screen's back buttons are clicked.
     */
    public void backToTownScreen(){
        switchPhase(Phase.MAIN_TURN);
        newPanelForFrame(townScreen, true);
        setInstructionText("You are back at the Town.  What would you like to do?");
    }
    
    
    /**
     * Generic method for handling the various cases when a user selects a tile
     * on the Main Screen. Contains logic to decide how the game will respond
     * to the tile selection.
     *
     * @param t The selected tile
     */
    public void tileSelected(Tile t){
        Logger.println("tile selected: " + t.toString());
        //decide what to do based on the game phase
        switch(phase){        
        case LAND_AUCTION:
            //the player wants to purchase the tile
            if (t.isOwned()){
                mainScreen.setBuyButtonDisabled();
            	doSound("src/mulegame/Sounds/buzzer.wav");
                setInstructionText("Land is already owned");
            }else{
                if (engine.playerCanBuyLand()){
                    mainScreen.setBuyButtonEnabled(engine.getLandCost());
                	doSound("src/mulegame/Sounds/click.wav");
                    setInstructionText("Press BUY to purchase selected land for $" + new Integer(engine.getLandCost()).toString());
                }else{
                        mainScreen.setBuyButtonDisabled();
                    	doSound("src/mulegame/Sounds/buzzer.wav");
                        setInstructionText("Insufficient funds to purchase land for $" + new Integer(engine.getLandCost()).toString());
                }
            }
            break;
        case LAND_AUCTION_OVER:
            //the land auction is over 
        	doSound("src/mulegame/Sounds/buzzer.wav");
            setInstructionText("You may not purchase land now");
            break;
        case PLACE_MULE:
        	if(t.isOwnedBy(engine.getCurrPlayer())){
        		if(t.hasMule()){
        	    	doSound("src/mulegame/Sounds/buzzer.wav");
        		    setInstructionText("You already have a mule there! Select a different spot");
        		} else {
                    t.placeMule(engine.getCurrPlayer().takeMule());
                	doSound("src/mulegame/Sounds/click.wav");
                    setInstructionText("You have placed your mule.  Click on Town or Continue to proceed");
                    switchPhase(Phase.MAIN_TURN);
                }
        	}		
        	else{
        	    engine.getCurrPlayer().takeMule();
            	doSound("src/mulegame/Sounds/buzzer.wav");
        		setInstructionText("You do not own the selected tile. You have lost your mule :P");
                switchPhase(Phase.MAIN_TURN);
        	}
        	break;
        			
        default:
            mainScreen.setBuyButtonDisabled();
        	doSound("src/mulegame/Sounds/click.wav");
            
        }
    }
    
    /**
     * Generic method for handling the player selecting the town on the Main
     * Screen.
     *
     */
    public void townClicked(){

    	//decide what to do based on the current game phase
        switch(phase){
        case LAND_AUCTION:
            //the player has tried to purchase the town in the land auction phase
        	doSound("src/mulegame/Sounds/buzzer.wav");
            setInstructionText("You cannot buy the town");
            break;
        case LAND_AUCTION_OVER:
            //do not set instruction text here because it will be set depending
            //on how the player got here
        	doSound("src/mulegame/Sounds/click.wav");
            break;
        case PLACE_MULE:
        	setInstructionText("You cannot place a mule on the town!");
        	doSound("src/mulegame/Sounds/buzzer.wav");
        	break;
        default:
            //show the Town Screen
        	doSound("src/mulegame/Sounds/click.wav");
            townScreen = new TownScreen(this, 1000*mainScreen.getTimeLeft());
            newPanelForFrame(townScreen, true);
            setInstructionText("This town is still a little boring.  You can go to the Pub and Store or Save/Load a game!!");
        }
    }

    /**
     * Generic method for handling buy button click events. Decides what to do  
     * based on the current game phase. 
     *
     */
    public void buyClicked(){
    	
    	//decide what to do depending on the current phase
        switch(phase){
        case LAND_AUCTION:
            //typical case, actually buy the tile
            if (engine.buyLand(mainScreen.getSelectedTile())){
            	doSound("src/mulegame/Sounds/cashregister.wav");   
                setInstructionText("Land purchased for $" + new Integer(engine.getLandCost()).toString() + "  Press Continue to proceed");
                switchPhase(Phase.LAND_AUCTION_OVER);
                ((Resources)panelResc).usePlayer(engine.getCurrPlayer());
            }else{
            	doSound("src/mulegame/Sounds/buzzer.wav");   
                setInstructionText("Insufficient funds");   
            }
            break;
        case STORE:
            Store.ItemType type = ((StoreScreen)storeScreen).getItemType();
            Mule m = null;
            switch(type){
            case MULE_FOOD:
                m = Mule.FOOD;
            case MULE_ENERGY:
                m = m == null ? Mule.ENERGY : m;
            case MULE_ORE:
                m = m == null ? Mule.ORE : m;
                if(engine.buyMule(m)){
                    switchPhase(Phase.PLACE_MULE);
                    doSound("src/mulegame/Sounds/cashregister.wav");  
                    backToMainScreen();
                } else {
                	doSound("src/mulegame/Sounds/buzzer.wav");   
                    setInstructionText("Insufficient funds");   
                }
                break;
            default:
                if(engine.storeSellTo(type, ((StoreScreen)storeScreen).getQuantity())){
                	doSound("src/mulegame/Sounds/cashregister.wav");  
                    setInstructionText("Transaction complete");
                } else {
                	doSound("src/mulegame/Sounds/buzzer.wav");  
                    setInstructionText("Insufficient funds");   
                }
            }
            ((Resources)panelResc).usePlayer(engine.getCurrPlayer());
            break;
        default:
            //should not be able to click buy outside of land auction phase, so 
            //log this error
        	doSound("src/mulegame/Sounds/buzzer.wav");  
        	Logger.println("buyClicked: how did you get here?");
            break;
        }
    }
    /**
     * Controls what happens when player clicks on store, lets player sell to store in store phase unless they
     *  don't have enough money or they are not in store phase. 
     */
    public void sellClicked(){
        switch(phase){
        case STORE:
            if(engine.storeBuyFrom(((StoreScreen)storeScreen).getItemType(), ((StoreScreen)storeScreen).getQuantity())){
            	doSound("src/mulegame/Sounds/cashregister.wav");  
                setInstructionText("Transaction complete");
            } else {
                //this is an error
            	doSound("src/mulegame/Sounds/buzzer.wav");  
                Logger.println("sellClicked: player did not have enough of selected resource");
            }
            ((Resources)panelResc).usePlayer(engine.getCurrPlayer());
            break;
        default:
        	doSound("src/mulegame/Sounds/buzzer.wav");  
            Logger.println("sellClicked: how did you get here?");
            break;
        }
    }
    
    
   /**
    * Generic method for handling the Pub button's click event. Changes the 
    * screen to the Pub Screen depending on the current game phase.
    *
    */
    public void pubClicked(){
    	//decide what to do based on the phase
        switch(phase){
        case LAND_AUCTION:
        case LAND_AUCTION_OVER:
            //the player should not be able to access the pub button 
            //in these phases, so log this error
        	doSound("src/mulegame/Sounds/buzzer.wav");  
            Logger.println("pubClicked: how did you get here!?");
            break;
        default:
            pubScreen = new PubScreen(this, 1000*mainScreen.getTimeLeft());
            newPanelForFrame(pubScreen, true);
            doSound("src/mulegame/Sounds/click.wav");
            break;
        }
    }
    
    
    /**
     * Generic method for handling the Pub Screen's gamble button click event.
     * If the current phase is correct, actually calculates the bonus and 
     * informs the player of the winnings.
     *
     */
    public void gambleClicked(){
    	int bonus = 0;
    	
    	//decide what to do based on the current phase
        switch(phase){
        case LAND_AUCTION:
        case LAND_AUCTION_OVER:
            //this should not be possible
        	doSound("src/mulegame/Sounds/buzzer.wav");
            Logger.println("gamble: how did you get here?");
            break;
        default:
            //calculate the bonus based on the time remaining for the turn
           bonus = engine.gamble(mainScreen.getTimeLeft());
           doSound("src/mulegame/Sounds/slotmachine.wav");
           JOptionPane.showMessageDialog(mainScreen, engine.getCurrentPlayerName() + " just won " + Integer.toString(bonus) + "!!", "CONGRATS!",  JOptionPane.WARNING_MESSAGE);
           continueClicked();
           break;
        }
    }
    
    /**
     * controls sound for when pass is clicked and controls user interaction with pass based on the 
     * gameplay
     */
    public void passClicked(){   	
    	doSound("src/mulegame/Sounds/click.wav");
        switch(phase){
        case LAND_AUCTION:
            switchPhase(Phase.LAND_AUCTION_OVER);
            if(engine.getRound() <= 2){
                setInstructionText("You have passed on free land.  Click Continue to proceed");
            }else{
                setInstructionText("You have passed on purchasing land, press continue");
                passCount++;
            }
            break;
        default:
            break;
        }
    }
    
    /**
     * controls sound for when continue is clicked and controls user interaction with continue based on the 
     * gameplay
     */
    public void continueClicked(){
    	doSound("src/mulegame/Sounds/click.wav");
        switch(phase){
        case LAND_AUCTION:
            setInstructionText("You must purchase land or click pass");
            break;
        default:
            finishMainScreen();
            break;
        }
    }
    
    /**
     * plays sound for when store is clicked, switches phase to store phase, creates store screen
     */
    public void storeClicked(){
       doSound("src/mulegame/Sounds/click.wav");
       switch(phase){
       case LAND_AUCTION:
       case LAND_AUCTION_OVER:
       case PLACE_MULE:
           //these phases should never allow the player to get to the store, so
           //log this error
           Logger.println("storeClicked: how did you get here?");
           break;
       default:
           switchPhase(Phase.STORE);
           storeScreen = new StoreScreen(this, 1000*mainScreen.getTimeLeft(), engine.getStore(), engine.getCurrPlayer());
           newPanelForFrame(storeScreen, true);
           break;
       }
    }
    
    
    // ########################################
    // #                                      #
    // #        convenience methods           #
    // #                                      #
    // ########################################
    /**
     * returns cost of food
     * @return engine.getFoodCost
     */
    public int getFoodCost(){
 	   return engine.getFoodCost();
    }
    /**
     * returns cost of energy
     * @return engine.getEnergyCost
     */
    public int getEnergyCost(){
 	   return engine.getEnergyCost();
    }
    /**
     * returns cost of ore
     * @return engine.getOreCost
     */
    public int getOreCost(){
 	   return engine.getOreCost();
    }
    /**
     * returns cost of mule
     * @return engine.getMuleCost
     */
    public int getMuleCost(Mule m){
    	return engine.getMuleCost(m);
    }
    
    /**
     * returns number of players
     * 
     * @return engine.getNumPlayers();
     */
    public int getNumPlayers(){
    	return engine.getNumPlayers();
    }
    /**
     * returns player color based on game engine info
     * 
     * @param p player
     * @return engine.getPlayerColor(p); returns color of player p
     */
    
    public Color getPlayerColor(int p){
    	return engine.getPlayerColor(p);
    }
    /**
     * returns player amount of money based on game engine info
     * 
     * @param p player
     * @return engine.getPlayerMoney(p); returns player money
     */
    
    public int getPlayerMoney(int p){
    	return engine.getPlayerMoney(p);
    }
    /**
     * game engine returns which player is currently playing 
     * @return engine.getCurrPlayer() returns the current player
     */
    public Player getCurrPlayer(){
    	return engine.getCurrPlayer();
    }
    
    /**
     * gets current players resources as an array
     * @return returns resources as an array
     */
    public int[] getCurrPlayerResources(){
 	   return engine.getCurrPlayerResources();
    }
    
    /**
     * gets current player's name
     * @return returns player's name
     */
    public String getCurrPlayerName(){
    	return engine.getCurrentPlayerName();
    }
    
    /**
     * game engine returns what round it currently is 
     * @return engine.getRound() returns what round it is
     */
    public int getRound(){
    	return engine.getRound();
    }
    /**
     * engine returns the players name 
     * @param p player
     * @return engine.getPlayerName(p) returns player p's name
     */
    public String getPlayerName(int p){
    	return engine.getPlayerName(p);
    }
    
    /**
     * engine object returns the player's score
     * 
     * @param p player
     * @return engine.getPlayerScore(p) returns score of player p
     */
    public int getPlayerScore(int p){
    	return engine.getPlayerScore(p);
    }
    
    // ########################################
    // #                                      #
    // #           private methods            #
    // #      don't need to be javadoc'd      #
    // #                                      #
    // ########################################
    
    
    //goes to the next turn, or the next round if everyone has had a turn
    private void incrTurn(){
        turnCount++;
        String msgText = "";
        //deal with some phases that the turn could time-out on
        switch(phase){
        case STORE:
        case PLACE_MULE:
            switchPhase(Phase.MAIN_TURN);
            break;
        default:
            break;
        } 
        if (turnCount >= getNumPlayers()){  //if the round is over
            //reset turn count
            turnCount = 0;
            //go to the next phase depending on the current phase
            switch(phase){
            case LAND_AUCTION:
            case LAND_AUCTION_OVER:
                if(engine.getRound() > 2){
                    if (passCount >= getNumPlayers()){
                        Logger.println("everyone has passed, switching to main turn phase");
                        switchPhase(Phase.MAIN_TURN);
                    }else{
                        Logger.println("not everyone has passed\npassCount = " + new Integer(passCount).toString() + "\ndoing land auction again");
                        switchPhase(Phase.LAND_AUCTION);
                    }
                    passCount = 0;
                    
                }else{
                    switchPhase(Phase.MAIN_TURN);
                }
                break;
            case MAIN_TURN:
                //the round is actually over
                
                //start a new land auction
                switchPhase(Phase.LAND_AUCTION);
                //notify the model of the round change
                engine.incrRound();
                ((Scores)panelScores).update();
                break;
            default:
                break;
            }
        }
        engine.setTurn(turnCount);
        switch(phase){
        case MAIN_TURN:
            msgText = " get ready for your main turn";
            break;
        case LAND_AUCTION:
        case LAND_AUCTION_OVER:
            msgText = " prepare for the land auction";
            break;
        case INIT:
            //do nothing
            break;
        default:
            Logger.println("turn change pop-up message switch: unknown phase");
            break;
        }
        if(phase != Phase.INIT){
            JOptionPane.showMessageDialog(gameFrame, engine.getCurrentPlayerName() + ":" + msgText, "Next Turn", JOptionPane.WARNING_MESSAGE);
        }
        if (phase == Phase.MAIN_TURN){
        	Logger.println("Random Event Called [27% Chance]");
        	int i = engine.randomEvent();
        	int m = engine.getRoundRandomEventFactor();
        	switch(i){
        	case 0:
        		JOptionPane.showMessageDialog(gameFrame, engine.getCurrentPlayerName() + ", YOU JUST RECEIVED A PACKAGE FROM THE GT ALUMNI CONTAINING 3 FOOD AND 2 ENERGY UNITS.",  "ENJOY!", JOptionPane.WARNING_MESSAGE);
				break;
			case 1:
        		JOptionPane.showMessageDialog(gameFrame, engine.getCurrentPlayerName() + ", A WANDERING TECH STUDENT REPAID YOUR HOSPITALITY BY LEAVING TWO BAGS OF BEANS.",  "ENJOY!", JOptionPane.WARNING_MESSAGE);
				break;
			case 2:
        		JOptionPane.showMessageDialog(gameFrame, engine.getCurrentPlayerName() + ", THE MUSEUM BOUGHT YOUR ANTIQUE PERSONAL COMPUTER FOR $" + Integer.toString(8*m),  "ENJOY!", JOptionPane.WARNING_MESSAGE);
				break;
			case 3:
        		JOptionPane.showMessageDialog(gameFrame, engine.getCurrentPlayerName() + ", YOU FOUND A DEAD MOOSE RAT AND SOLD THE HIDE FOR $" + Integer.toString(2*m),  "ENJOY!", JOptionPane.WARNING_MESSAGE);
				break;
			case 4:
        		JOptionPane.showMessageDialog(gameFrame, engine.getCurrentPlayerName() + ", FLYING CAT-BUGS ATE THE ROOF OFF YOUR HOUSE. REPAIRS COST $" + Integer.toString(4*m),  "OH NO!", JOptionPane.WARNING_MESSAGE);
				break;
			case 5:
        		JOptionPane.showMessageDialog(gameFrame, engine.getCurrentPlayerName() + ", MISCHIEVOUS UGA STUDENTS BROKE INTO YOUR STORAGE SHED AND STOLE HALF YOUR FOOD.",  "OH NO!", JOptionPane.WARNING_MESSAGE);
				break;
			case 6:
        		JOptionPane.showMessageDialog(gameFrame, engine.getCurrentPlayerName() + ", YOUR SPACE GYPSY INLAWS MADE A MESS OF THE TOWN. IT COST YOU $" + Integer.toString(8*m) + " TO CLEAN IT UP.",  "OH NO!", JOptionPane.WARNING_MESSAGE);
				break;
        	}
        }
        Logger.println("turnCount: " + new Integer(turnCount).toString());
    }

    //changes the phase and handles changing the currently displayed screen
    //accordingly
    private void switchPhase(Phase p){
        phase = p;
        switch(p){
        case LAND_AUCTION:
            //buy button starts off disabled
            mainScreen.setBuyButtonDisabled();
            //pass button starts off enabled
            mainScreen.setPassButtonEnable(true);
            //give the player some instruction
            setInstructionText("Select a plot of land to purchase");
            break;
        case LAND_AUCTION_OVER:
            //disable buy and pass buttons
            mainScreen.setBuyButtonDisabled();
            mainScreen.setPassButtonEnable(false);
            
            //do not set the instruction text here because how the player got
            //to this phase will determine what instruction is shown
            break;
        case MAIN_TURN:
            //disable buy and pass buttons
            mainScreen.setBuyButtonDisabled();
            mainScreen.setPassButtonEnable(false);
            
            mainScreen.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
            
            //do not set instructions here because the instruction will be set
            //by the calling method
            break;
            
        case PLACE_MULE:
            //disable buy and pass buttons
            mainScreen.setBuyButtonDisabled();
            mainScreen.setPassButtonEnable(false);
            
            Toolkit toolkit = Toolkit.getDefaultToolkit(); 
            Image image = toolkit.getImage("src/mulegame/Images/mule.png"); 
            Point hotSpot = new Point(5,5); 
            Cursor cursor = toolkit.createCustomCursor(image, hotSpot, "Mule");
            
            mainScreen.setCursor(cursor);
            
            //give the player some instruction
            setInstructionText("Place your mule. BE CAREFUL if you place a mule on a property you don't own, you will LOSE YOUR MULE!");
            break;
            
        case STORE:
            //give the player some instruction
            setInstructionText("Select the type and quantity of item(s) to purchase/sell");
            break;
            
        //TODO more phases to come
        
        //if the phase that is given has not been implemented here, log this error
        default:
            Logger.println("switchPhase given an unknown phase");
            break;
        }
    }

	//plays the sound file specified by soundName
    //soundName should be a file path
    private void doSound(String soundName) {
    	AudioInputStream audioInputStream = null;
    	Clip clip = null;
		try {
			audioInputStream = AudioSystem.getAudioInputStream(new File(soundName).getAbsoluteFile());
			clip = AudioSystem.getClip();
			clip.open(audioInputStream);
		} catch (Exception e) {
		    Logger.println("audio exception");
		}
    	clip.start();
    }

    //gets called during LAND_AUCTION, LAND_AUCTION_OVER, and MAIN_TURN phases
    //if this is called during any other phase, puts a notification about this error 
    //in the log
    private void showNextScreen(){
        //make a decision about what screen to show next based on the 
        //current game phase
        Logger.println("showNextScreen");
        switch(phase){
        case LAND_AUCTION:
        case LAND_AUCTION_OVER:
            Logger.println("\tshowing land auction");
            switchPhase(Phase.LAND_AUCTION);
            startLandAuction();
            break;
        case MAIN_TURN:
            Logger.println("\tshowing main turn");
            beginMainTurn();
            break;
        default:
            Logger.println("\tunknown phase, how did you get here?");
            break;
        }
    }
    
    
    /**
     * sets the text in the Instructions panel. concats the given message with the current players name
     * this should effect all screens that have the Instructions panel.
     * @param msg message to be shown w/o player name
     */
       
    public void setInstructionText(String msg){
        ((Instructions)panelInstr).setText(engine.getCurrentPlayerName() + ": " + msg);        
    }
    
    /**
     * chooses a file and writes game to that file. If file can't be saved, error message printed. Otherwise, 
     * game is saved and message is printed out
     */
    public void saveGame(){
        Logger.println("Saving...");
        JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int returnValue = chooser.showSaveDialog(gameFrame);
        if(returnValue == JFileChooser.APPROVE_OPTION){
            PrintWriter writer;
            try{
                writer = new PrintWriter(new File(chooser.getCurrentDirectory(), chooser.getSelectedFile().getName()));
            }catch (Exception e) {
                Logger.println("File exception, unable to save");
                return;
            }
            //start saving the game with the given file
            writer.write(engine.saveGame());
            writer.flush();
            
        }
        Logger.println("Done");
    }
    
    /**
     * chooses from files and loads game after scanning through file directory. If file can't bee found an exception 
     * will be thrown. Otherwise the file will be loaded.
     */
    public void loadGame(){
        Logger.println("Loading...");
        JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int returnValue = chooser.showOpenDialog(gameFrame);
        if(returnValue == JFileChooser.APPROVE_OPTION){
            Scanner scan;
            try{
                scan = new Scanner(new File(chooser.getCurrentDirectory(), chooser.getSelectedFile().getName()));
            }catch (Exception e) {
                Logger.println("File exception, unable to load");
                return;
            }
            engine.loadGame(scan);
            turnCount = engine.getTurn();
            
            panelScores = new Scores(engine.getPlayers());
            panelResc = new Resources();
            beginMainTurn();
            
        }
        Logger.println("Done");
    }
}
